#pragma once

#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include<unistd.h>
#include<cstdlib>
#include<sstream>
#include<sys/types.h>
#include<algorithm>
#include<sys/sendfile.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/wait.h>
#include"Util.hpp"
#include"Log.hpp"

//#define DEBUG
#define SEP ": "
#define OK 200
#define NOT_FOUND 404
#define LINE_END "\r\n"//报头行分隔符
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define PAGE_400 "400.html"
#define PAGE_403 "403.html"
#define PAGE_404 "404.html"
#define PAGE_500 "500.html"
#define PAGE_504 "504.html"
#define HTTP_VERSION "HTTP/1.0"

#define OK 200
#define MOVED_PERMANENTLY 301
#define TEMPORARY_REDIRECT 307
#define BAD_REQUEST 400
#define FORBIDDEN 403
#define NOT_FOUND 404
#define SERVER_ERROR 500
#define BAD_GATEWAY 504

//状态码转描述
static std::string CodeToDesc(int code)
{
    switch (code)
    {
    case 200:
        return "OK";
        break;
    case 404:
        return "NOT FOUND";
    default:
        return "";
        break;
    }
    return "";
}
//文件后缀标识符转描述
static std::string SuffixToDesc(const std::string& suffix)
{
    static std::unordered_map<std::string,std::string> stod={
        {".html","text/html"},
        {".htm","text/html"},
        {".js","application/x-javascript"},
        {".css","text/css"},
        {".jpg","image/jpeg"},
    };
    auto it=stod.find(suffix);
    if(it!=stod.end()) 
        return stod[suffix];
    else 
        return "text/html";
}
//请求
class Request
{
public:
    std::string request_line;
    std::vector<std::string> request_header;
    std::string blank;
    std::string request_body;
    //解析后的结果
    std::string method;
    std::string url;
    std::string version;

    std::unordered_map<std::string,std::string> header_kv;
    int content_length=0;

    std::string path;//访问路径
    std::string query_str;//参数
    bool cgi=false;//是否需要使用cgi数据参数处理机制
};
//响应
class Response
{
public:
    std::string status_line;
    std::vector<std::string> request_header;
    std::string blank=LINE_END;
    std::string response_body;
    int status_code=OK;
    int fd=-1;//要打开的访问文件
    int size=0;//访问资源大小
    std::string suffix=".html";//访问资源后缀
};
//读取，分析，构建响应，IO通信
class EndPoint
{
private:
    int sock;
    bool stop=false;//遇到错误-是否停止运行
    Request request;
    Response response;
private:
    bool RecvRequestLine()
    {
        if(Util::ReadLine(sock,request.request_line)<0){
            stop=true;
        }
        LOG(INFO,request.request_line);
        return stop;
    }
    bool RecvRequestHeader()
    {
        std::string line;
        while(line!="\n")
        {
            line.clear();
            int ret=Util::ReadLine(sock,line);
            if(ret>0)
                request.request_header.push_back(line);
            else 
            {
                if(ret<0)
                    stop=true;
                break;
            }
            LOG(INFO,line);
        }
        if(line=="\n") request.blank=line;
        return stop;
    }
    void ParseRequestLine()
    {
        std::stringstream ss(request.request_line);
        ss>>request.method>>request.url>>request.version;
        //更新字符大小-格式成大写
        std::transform(request.method.begin(),request.method.end(),request.method.begin(),toupper);
        LOG(INFO,request.method);
        LOG(INFO,request.url);
        LOG(INFO,request.version);
    }
    void ParseRequestHeader()
    {
        std::string key;
        std::string value;
        for(auto& iter:request.request_header)
        {
            if(Util::CutStr(iter,key,value,SEP))
            {
                request.header_kv.insert({key,value});
                LOG(INFO,key);
                LOG(INFO,value);
            }
        }
    }
    bool isNeedRecvRequestBody()
    {
        //GET方法没有正文
        if(request.method=="POST")
        {
            if(request.header_kv.find("Content-Length")!=request.header_kv.end())
            {
                request.content_length=atoi(request.header_kv["Content-Length"].c_str());
                return true;
            }
        }
        return false;
    }
    void RecvRequestBody()
    {
        if(isNeedRecvRequestBody())
        {
            char c='X';
            int len=request.content_length;
            while(len)
            {
                ssize_t s=recv(sock,&c,1,0);
                if(s>0)
                {
                    request.request_body.push_back(c);
                    len--;
                }
                else
                    break;
            }
        }
    }
    int ProcessCgi()
    {
        LOG(INFO,"Process Cgi");
        auto& method=request.method;
        auto& path=request.path;
        auto& body=request.request_body;
        auto& code=response.status_code;
        //创建子进程，再进行程序替换执行对应的程序文件
        //创建管道用于父进程和子进程数据通信
        int input[2];//读写是站在父进程的角度命名的
        int output[2];
        if(pipe(input)<0){
            LOG(ERROR,"pipe input err");
            return 404;
        }
        if(pipe(output)<0){
            LOG(ERROR,"pipe output err");
            return 404;
        }
        pid_t pid=fork();
        if(pid==0)//child 
        {
            close(input[0]);
            close(output[1]);
            //在程序替换之前设置环境变量，环境变量具有全局属性-设置环境变量使得替换后的程序继承
            std::string Env_method="METHOD="+method;
            putenv((char*)Env_method.c_str());
           
            if(method=="GET")
            {
                std::string query="QUERY="+request.query_str;
                putenv((char*)query.c_str());//GET数据参数少，使用环境变量传送数据
            }
            else if(method=="POST")
            {
                std::string ConL="Content-Length="+std::to_string(request.content_length);
                putenv((char*)ConL.c_str());//POST父进程发送数据，但是使用环境变量告诉子进程参数数据大小
            }
            //子进程的标准输入重定向为父进程写入；子进程的标准输出重定向为父进程读入
            dup2(input[1],1);
            dup2(output[0],0);
            //进行程序替换
            execl(path.c_str(),path.c_str(),nullptr);
            LOG(ERROR,"execl err");
            exit(5);
        }
        else if (pid>0){
            close(input[1]);
            close(output[0]);
            if(method=="POST")//POST通过管道发送数据给子进程进行处理
            {
                int size=0,total=0;
                while((size=write(output[1],body.c_str()+total,body.size()-total)>0))
                    total+=size;
                //读取结果
                char c='X';
                while(read(input[0],&c,1)>0)
                    response.response_body+=c;
                //等待子进程结束
                int status;
                pid_t ret=waitpid(pid,&status,0);
                if(ret>0)//等待成功
                {
                    if(WIFEXITED(status))//正常退出
                    {  
                        if(WEXITSTATUS(status)==0)//退出码正常
                        {
                            code=OK;
                        }
                        else//退出码异常
                        {
                            LOG(INFO,"code err");
                        }
                    }
                    else//异常退出
                    {
                        LOG(INFO,"exit err");
                    }
                    
                }
                else//等待失败
                {
                    LOG(INFO,"wait err");
                }
                //关闭管道文件
                close(input[0]);
                close(output[1]);
            }
            
        }
        else{
            LOG(ERROR,"fork err");
            return 404;
        }
        return code;
    }
    int ProcessNonCgi()
    {
        response.fd=open(request.path.c_str(),O_RDONLY);
        if(response.fd>=0)
            return OK;
        return NOT_FOUND;
    }
    //构建正确的响应报头
    void HandlerOKResponse()
    {
        std::string content_type="Content-Type: ";
        content_type+=SuffixToDesc(response.suffix)+LINE_END;
        response.request_header.push_back(content_type);

        std::string content_length="Content-Length: ";
        if(request.cgi){//带有参数处理
            content_length+=std::to_string(response.response_body.size())+LINE_END;
        }
        else{//不带参数-返回页面资源
            content_length+=std::to_string(response.size)+LINE_END;
        }
        response.request_header.push_back(content_length);
    }
    //统一进行差错处理
    void HandlerError(std::string page)
    {
        LOG(INFO,"HandlerError...");
        //所有的差错处理返回页面
        request.cgi=false;
        //打开错误页面进行返回
        page=WEB_ROOT+std::string("/")+page;
        LOG(INFO,"HandlerError:"+page);
        response.fd=open(page.c_str(),O_RDONLY);
        if(response.fd>0)
        {
            struct stat st;
            stat(page.c_str(),&st);
            response.size=st.st_size;
            //处理响应报头
            std::string content_type="Content-Type: ";
            content_type+=SuffixToDesc(".html")+LINE_END;
            response.request_header.push_back(content_type);
            std::string content_length="Content-Length: ";
            content_length+=std::to_string(response.size)+LINE_END;
            response.request_header.push_back(content_length);
            std::cout<<content_type<<std::endl;
            std::cout<<content_length<<std::endl;
        }
    }
public:
    EndPoint(int _sock):sock(_sock){}
    ~EndPoint()
    {
        close(sock);
    }
    bool isStop()
    {
        return stop;
    }
    //获取请求并进行分析
    void RecvRequest()
    {
        if((!RecvRequestLine())&&(!RecvRequestHeader()))//读取请求没问题
        {
            ParseRequestLine();
            ParseRequestHeader();
            RecvRequestBody();
        }
    }
    void BuildResponseHelper()
    {
        auto& code=response.status_code;
        //状态行构建：版本-状态码-状态描述
        response.status_line+=HTTP_VERSION;
        response.status_line+=" "+std::to_string(code);
        response.status_line+=" "+CodeToDesc(code);
        response.status_line+=LINE_END;
        //根据状态码处理
        switch(code)
        {
            case OK:
                HandlerOKResponse();
                break;
            case BAD_REQUEST:
                HandlerError(PAGE_400);
                break;
            case NOT_FOUND:
                HandlerError(PAGE_404);
                break;
            case SERVER_ERROR:
                HandlerError(PAGE_500);
                break;
             case BAD_GATEWAY:
                HandlerError(PAGE_504);
                break;
            default:
                break;
        }
    }
    //根据分析请求后进行逻辑处理并构建响应
    void BuildResponse()
    {
        LOG(INFO,"build response");
        std::string _path;
        auto& code=response.status_code;
        int pos=-1;
        //1.依据请求方法处理
        if(request.method!="GET"&&request.method!="POST")//暂且认定为非法方法
        {
            code=NOT_FOUND;
            LOG(WARNING,"method err");
            goto END;
        }
        else if(request.method=="GET")
        {
            //进行切分路径和参数
            auto pos=request.url.find('?');
            if(pos!=std::string::npos)
            {
                Util::CutStr(request.url,request.path,request.query_str,"?");
                //带有参数需要使用cgi机制处理
                request.cgi=true;
            }
            else
                request.path=request.url;
        }
        else if(request.method=="POST")//post方法带有数据
        {
            request.cgi=true;
            request.path=request.url;//POST的url就是path
        }
        //2.处理访问资源路径
        _path=request.path;
        request.path=WEB_ROOT; 
        request.path+=_path;
        if(request.path[request.path.size()-1]=='/')// 未知名具体文件资源时，默认返回当前目录下的首页 
            request.path+=HOME_PAGE;
        std::cout<<request.path<<std::endl;
        std::cout<<request.query_str<<std::endl;
        //3.分析访问资源是否存在以及其类型
        struct stat st;
        if(stat(request.path.c_str(),&st)==0)//资源是否存在
        {
            //目录资源
            if(S_ISDIR(st.st_mode))//将访问路径添加目录下的默认文件
            {
                request.path+="/";
                request.path+=HOME_PAGE;
                stat(request.path.c_str(),&st);//更新路径后需要更新下文件结构
            }
            //可执行文件
            if((st.st_mode&S_IXUSR)||(st.st_mode&S_IXGRP)||(st.st_mode&S_IXOTH))//可执行文件
                request.cgi=true;
            //填充文件的大小
            response.size=st.st_size;
        }
        else{
            LOG(WARNING,request.path+" NOT FOUND!");
            code=NOT_FOUND;
            goto END;
        }
        //4.处理参数
        //后缀分析->构建响应的报头
        pos=request.path.rfind('.');
        if(pos!=std::string::npos)                        
            response.suffix=request.path.substr(pos);
        //处理方式：带数据-cgi机制；不带数据返回对应资源
        if(request.cgi)
            code=ProcessCgi();
        else
            code=ProcessNonCgi();
END:          
        // 统一处理，构建响应
        BuildResponseHelper();                                                         
        return; 
    }
    //发送响应报头
    void SendResponse()
    {
        std::cout<<"SendResponse..."<<std::endl;
        //状态行
        send(sock,response.status_line.c_str(),response.status_line.size(),0);
        //报头
        for(auto& iter:response.request_header)
            send(sock,iter.c_str(),iter.size(),0);
        //空格
        send(sock,response.blank.c_str(),response.blank.size(),0);
        //报文
        if(request.cgi)//cgi处理
        {
            int size=0,total=0;
            while((size=send(sock,response.response_body.c_str(),response.response_body.size()-total,0))>0)
                total+=size;
        }
        else//返回文件资源
        {
            sendfile(sock,response.fd,nullptr,response.size);
            close(response.fd);//关闭打开的文件
        }
    }
};
//处理连接的入口-处理请求整个流程
class CallBack
{
public:
    void operator()(int sock)
    {
        HandlerRequest(sock);
    }
    void HandlerRequest(int sock)
    {
        LOG(INFO,"HandlerRequest Begin");
#ifdef DEBUG
        char buffer[4096]={0};
        ssize_t s=recv(sock,buffer,sizeof(buffer),0);
        buffer[s]=0;
        std::cout<<"------------------------------------------------"<<std::endl;
        std::cout<<buffer<<std::endl;
        std::cout<<"------------------------------------------------"<<std::endl;
#else
        EndPoint* ep=new EndPoint(sock);
        ep->RecvRequest();//接收+分析数据
        if(!ep->isStop())
        {
            LOG(INFO,"Recv No Error");
            ep->BuildResponse();//构建响应
            ep->SendResponse();//发送数据
        }
        else
            LOG(WARNING,"Recv Error");
        delete ep;
#endif
        LOG(INFO,"HandlerRequest End");
    }
};